﻿using System.IO;
using System;
using System.Text;
using System.Net;
namespace GxFramework
{
    public class ByteStream
    {
        public static readonly int Bool_Size = sizeof(byte);
        public static readonly int Byte_Size = sizeof(byte);
        public static readonly int Int16_Size = sizeof(Int16);
        public static readonly int UInt16_Size = sizeof(UInt16);
        public static readonly int Int32_Size = sizeof(Int32);
        public static readonly int UInt32_Size = sizeof(UInt32);
        public static readonly int Int64_Size = sizeof(Int64);
        public static readonly int UInt64_Size = sizeof(UInt64);
        public static readonly int Float_Size = sizeof(float);
        public static readonly int Double_Size = sizeof(double);
        
        /* 
         * 主机一般是低位有效字节在前：小端模式
         * 网络一般是高位有效字节在前：大端模式
         * 默认是小端模式：网络字节读写需要逆转字节顺序
         */
        bool _isBigEndian = false;
        Encoding _encoding;
        private byte[] _bytes;
        int _position;
        int _length;
        public ByteStream(int len)
        {
            if (len > 0)
                Reset(new byte[len]);
        }
        public ByteStream(byte[] bytes = null)
        {
            Reset(bytes);
        }
        public Encoding encoding
        {
            get { return _encoding ?? Encoding.Default; }
            set { _encoding = value; }
        }
        public void Reset(byte[] bytes = null)
        {
            _position = 0;
            _bytes = bytes;
            _length = (null == _bytes) ? 0 : _bytes.Length;
        }
        public bool isBigEndian
        {
            get { return _isBigEndian; }
            set { isBigEndian = value; }
        }
        private bool needReverseBytes { get { return _isBigEndian; } }
        public int position
        {
            get
            {
                return _position;
            }
            set
            {
                if (value < 0)
                {
                    _position = 0;
                }
                else if (value > length)
                {
                    _position = length;
                }
                else
                {
                    _position = value;
                }
            }
        }
        public int capacity { get { return null == _bytes ? 0 : _bytes.Length; } }
        public int length
        {
            get
            {
                return (null == _bytes) ? 0 : _length;
            }
            set
            {
                if (null != _bytes)
                {
                    _length = Math.Min(_bytes.Length, value);
                }
            }
        }
        public int bytesAvailable
        {
            get
            {
                if (_position >= length)
                    return 0;
                return _length - _position;
            }
        }
        public byte[] buffer
        {
            get
            {
                return _bytes;
            }
        }
        public byte this[int i]
        {
            get
            {
                if(i >= this.length)
                    throw new IndexOutOfRangeException();
                return _bytes[i];
            }
            set
            {
                if (i >= this.length)
                    throw new IndexOutOfRangeException(); 
                _bytes[i] = value;
            }
        }
        public void Clear()
        {
            Clear(false);
        }
        public void Clear(bool clearBuffer)
        {
            _position = 0;
            _length = 0;
            if (clearBuffer)
                _bytes = null;
        }

        public byte ReadByte()
        {
            if (this.bytesAvailable < Byte_Size)
            {
                throw new IndexOutOfRangeException();
            }
            int result = _bytes[_position];
            _position += Byte_Size;
            return (byte)result;
        }
        public bool ReadBool()
        {
            if (this.bytesAvailable < Bool_Size)
            {
                throw new IndexOutOfRangeException();
            }
            int result = _bytes[_position];
            _position += Bool_Size;
            return 0 != result;
        }
        public short ReadInt16()
        {
            int loop = UInt16_Size;

            if (this.bytesAvailable < loop)
            {
                throw new IndexOutOfRangeException();
            }

            int result = 0;

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                result |= (_bytes[_position + offset] << (i * 8));
            }
            this._position += loop;
            return (short)result;
        }
        public ushort ReadUInt16()
        {
            int loop = Int16_Size;

            if (this.bytesAvailable < loop)
            {
                throw new IndexOutOfRangeException();
            }

            int result = 0;

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                result |= (_bytes[_position + offset] << (i * 8));
            }
            this._position += loop;
            return (ushort)result;
        }
        public int ReadInt32()
        {
            int loop = Int32_Size;

            if (this.bytesAvailable < loop)
            {
                throw new IndexOutOfRangeException();
            }

            int result = 0;

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                result |= ( _bytes[_position + offset] << (i * 8) );
            }
            this._position += loop;
            return result;
        }
        public uint ReadUInt32()
        {
            int loop = UInt32_Size;

            if (this.bytesAvailable < loop)
            {
                throw new IndexOutOfRangeException();
            }

            uint result = 0;

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            uint tmp;
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                tmp = _bytes[_position + offset];
                result |= (tmp << (i * 8));
            }
            this._position += loop;
            return result;
        }
        public long ReadInt64()
        {
            int loop = Int64_Size;

            if (this.bytesAvailable < loop)
            {
                throw new IndexOutOfRangeException();
            }

            long result = 0;

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            long tmp;
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                tmp = _bytes[_position + offset];
                result |= (tmp << (i*8));
            }
            this._position += loop;
            return result;
        }
        public ulong ReadUInt64()
        {
            int loop = UInt64_Size;

            if (this.bytesAvailable < loop)
            {
                throw new IndexOutOfRangeException();
            }

            ulong result = 0;

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            ulong tmp;
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                tmp = _bytes[_position + offset];
                result |= (tmp << (i * 8));
            }
            this._position += loop;
            return result;
        }
        public float ReadFloat()
        {
            if (this.bytesAvailable < Float_Size)
            {
                throw new IndexOutOfRangeException();
            }
            float result = BitConverter.ToSingle(_bytes, _position);
            if (this.needReverseBytes)
            {
                byte[] bytes = BitConverter.GetBytes(result);
                Array.Reverse(bytes);
                result = BitConverter.ToSingle(bytes, 0);
            }
            this._position += Float_Size;
            return result;
        }
        public double ReadDouble()
        {
            if (this.bytesAvailable < Double_Size)
            {
                throw new IndexOutOfRangeException();
            }
            double result = BitConverter.ToDouble(_bytes, _position);
            if (this.needReverseBytes)
            {
                byte[] bytes = BitConverter.GetBytes(result);
                Array.Reverse(bytes);
                result = BitConverter.ToDouble(bytes, 0);
            }
            this._position += Double_Size;
            return result;
        }
        public string ReadString()
        {
            if (null == _bytes || _position >= _length)
            {
                throw new IndexOutOfRangeException();
            }
            ushort len = this.ReadUInt16();
            string result = this.encoding.GetString(_bytes, _position, len);
            this._position += len;
            return result;
        }

        public void Read(byte[] bytes, int offset = 0, int length = 0)
        {
            int numAvailable = this.bytesAvailable;
            
            int num = length > 0 ? length : numAvailable;
            
            if (num == 0)
                return;
            if (numAvailable < num)
                throw new IndexOutOfRangeException();

            Array.Copy(_bytes, _position, bytes, offset, num);
            _position += num;
        }

        ///**
        // ***************************** 分割线 ********************************************************
        // */
        public void WriteByte(byte value)
        {
            int newPostion = _position + Byte_Size;
            CheckAlloc(newPostion);
            _bytes[_position] = value;
            _position = newPostion;
            if (_position > _length)
                _length = _position;
        }
        public void WriteBool(bool value)
        {
            byte tmp = (byte)(value ? 1 : 0);
            this.WriteByte(tmp);
        }


        public void WriteInt16(short value)
        {
            int loop = Int16_Size;

            int newPostion = _position + loop;
            CheckAlloc(newPostion);

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i,offset+=gap)
            {
                _bytes[_position + offset] = (byte)(value & 0xff);
                value >>= 8;
            }
            
            _position = newPostion;
            
            if (_position > _length)
                _length = _position;
        }
        public void WriteInt32(int value)
        {
            int loop = Int32_Size;
            
            int newPostion = _position + loop;
            CheckAlloc(newPostion);

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                _bytes[_position + offset] = (byte)(value & 0xff);
                value >>= 8;
            }

            _position = newPostion;

            if (_position > _length)
                _length = _position;
        }
        public void WriteInt64(long value)
        {
            int loop = Int64_Size;

            int newPostion = _position + loop;
            CheckAlloc(newPostion);

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                _bytes[_position + offset] = (byte)(value & 0xff);
                value >>= 8;
            }

            _position = newPostion;

            if (_position > _length)
                _length = _position;
        }
        public void WriteUInt16(ushort value)
        {
            int loop = UInt16_Size;

            int newPostion = _position + loop;
            CheckAlloc(newPostion);

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                _bytes[_position + offset] = (byte)(value & 0xff);
                value >>= 8;
            }

            _position = newPostion;

            if (_position > _length)
                _length = _position;
        }
        public void WriteUInt32(uint value)
        {
            int loop = UInt32_Size;

            int newPostion = _position + loop;
            CheckAlloc(newPostion);

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                _bytes[_position + offset] = (byte)(value & 0xff);
                value >>= 8;
            }

            _position = newPostion;

            if (_position > _length)
                _length = _position;
        }
        public void WriteUInt64(ulong value)
        {
            int loop = UInt64_Size;

            int newPostion = _position + loop;
            CheckAlloc(newPostion);

            int offset = 0;
            int gap = 1;
            if (this.needReverseBytes)
            {
                offset = loop - 1;
                gap = -1;
            }
            for (int i = 0; i < loop; ++i, offset += gap)
            {
                _bytes[_position + offset] = (byte)(value & 0xff);
                value >>= 8;
            }

            _position = newPostion;

            if (_position > _length)
                _length = _position;
        }
        public void WriteFloat(float value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (this.needReverseBytes)
            {
                Array.Reverse(bytes);
            }
            this.Write(bytes, 0, bytes.Length);
        }
        public void WriteDouble(double value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (this.needReverseBytes)
            {
                Array.Reverse(bytes);
            }
            this.Write(bytes, 0, bytes.Length);
        }
        public void WriteString(string value)
        {
            byte[] bytes = this.encoding.GetBytes(value);
            this.WriteInt16((short)bytes.Length);
            this.Write(bytes);
        }
        public void Write(ByteStream bytes,int length = -1)
        {
            byte[] byteArr = bytes.buffer;
            this.Write(byteArr, bytes.position, length);
        }
        public void Write(byte[] bytes, int offset = 0, int length = -1)
        {
            int numBytes = (length < 0) ? (bytes.Length - offset) : length;

            int newPosition = _position + numBytes;

            CheckAlloc(newPosition);

            Array.Copy(bytes, offset, _bytes, _position, numBytes);

            _position = newPosition;

            if (_position > _length)
                _length = _position;
        }
        void CheckAlloc(int size)
        {
            if (null == _bytes || _bytes.Length < size)
            {
                byte[] newBytes = new byte[2 * size];
                if (null != _bytes)
                {
                    Array.Copy(_bytes, 0, newBytes, 0, _length);
                }
                _bytes = newBytes;
            }
        }
    }//end class
}